home *** CD-ROM | disk | FTP | other *** search
/ Graphics Plus / Graphics Plus.iso / msdos / viewers / hv12 / src / jmemmgr.c < prev    next >
C/C++ Source or Header  |  1992-06-19  |  37KB  |  1,050 lines

  1. /*
  2.  * jmemmgr.c
  3.  *
  4.  * Copyright (C) 1991, 1992, Thomas G. Lane.
  5.  * This file is part of the Independent JPEG Group's software.
  6.  * For conditions of distribution and use, see the accompanying README file.
  7.  *
  8.  * This file provides the standard system-independent memory management
  9.  * routines.  This code is usable across a wide variety of machines; most
  10.  * of the system dependencies have been isolated in a separate file.
  11.  * The major functions provided here are:
  12.  *   * bookkeeping to allow all allocated memory to be freed upon exit;
  13.  *   * policy decisions about how to divide available memory among the
  14.  *     various large arrays;
  15.  *   * control logic for swapping virtual arrays between main memory and
  16.  *     backing storage.
  17.  * The separate system-dependent file provides the actual backing-storage
  18.  * access code, and it contains the policy decision about how much total
  19.  * main memory to use.
  20.  * This file is system-dependent in the sense that some of its functions
  21.  * are unnecessary in some systems.  For example, if there is enough virtual
  22.  * memory so that backing storage will never be used, much of the big-array
  23.  * control logic could be removed.  (Of course, if you have that much memory
  24.  * then you shouldn't care about a little bit of unused code...)
  25.  *
  26.  * These routines are invoked via the methods alloc_small, free_small,
  27.  * alloc_medium, free_medium, alloc_small_sarray, free_small_sarray,
  28.  * alloc_small_barray, free_small_barray, request_big_sarray,
  29.  * request_big_barray, alloc_big_arrays, access_big_sarray, access_big_barray,
  30.  * free_big_sarray, free_big_barray, and free_all.
  31.  */
  32.  
  33. #define AM_MEMORY_MANAGER    /* we define big_Xarray_control structs */
  34.  
  35. #include "jinclude.h"
  36. #include "jmemsys.h"        /* import the system-dependent declarations */
  37.  
  38.  
  39. /*
  40.  * On many systems it is not necessary to distinguish alloc_small from
  41.  * alloc_medium; the main case where they must be distinguished is when
  42.  * FAR pointers are distinct from regular pointers.  However, you might
  43.  * want to keep them separate if you have different system-dependent logic
  44.  * for small and large memory requests (i.e., jget_small and jget_large
  45.  * do different things).
  46.  */
  47.  
  48. #ifdef NEED_FAR_POINTERS
  49. #define NEED_ALLOC_MEDIUM    /* flags alloc_medium really exists */
  50. #endif
  51.  
  52.  
  53. /*
  54.  * Some important notes:
  55.  *   The allocation routines provided here must never return NULL.
  56.  *   They should exit to error_exit if unsuccessful.
  57.  *
  58.  *   It's not a good idea to try to merge the sarray and barray routines,
  59.  *   even though they are textually almost the same, because samples are
  60.  *   usually stored as bytes while coefficients are shorts.  Thus, in machines
  61.  *   where byte pointers have a different representation from word pointers,
  62.  *   the resulting machine code could not be the same.
  63.  */
  64.  
  65.  
  66. static external_methods_ptr methods; /* saved for access to error_exit */
  67.  
  68.  
  69. #ifdef MEM_STATS        /* optional extra stuff for statistics */
  70.  
  71. /* These macros are the assumed overhead per block for malloc().
  72.  * They don't have to be accurate, but the printed statistics will be
  73.  * off a little bit if they are not.
  74.  */
  75. #define MALLOC_OVERHEAD  (SIZEOF(void *)) /* overhead for jget_small() */
  76. #define MALLOC_FAR_OVERHEAD  (SIZEOF(void FAR *)) /* for jget_large() */
  77.  
  78. static long total_num_small = 0;    /* total # of small objects alloced */
  79. static long total_bytes_small = 0;    /* total bytes requested */
  80. static long cur_num_small = 0;        /* # currently alloced */
  81. static long max_num_small = 0;        /* max simultaneously alloced */
  82.  
  83. #ifdef NEED_ALLOC_MEDIUM
  84. static long total_num_medium = 0;    /* total # of medium objects alloced */
  85. static long total_bytes_medium = 0;    /* total bytes requested */
  86. static long cur_num_medium = 0;        /* # currently alloced */
  87. static long max_num_medium = 0;        /* max simultaneously alloced */
  88. #endif
  89.  
  90. static long total_num_sarray = 0;    /* total # of sarray objects alloced */
  91. static long total_bytes_sarray = 0;    /* total bytes requested */
  92. static long cur_num_sarray = 0;        /* # currently alloced */
  93. static long max_num_sarray = 0;        /* max simultaneously alloced */
  94.  
  95. static long total_num_barray = 0;    /* total # of barray objects alloced */
  96. static long total_bytes_barray = 0;    /* total bytes requested */
  97. static long cur_num_barray = 0;        /* # currently alloced */
  98. static long max_num_barray = 0;        /* max simultaneously alloced */
  99.  
  100.  
  101. LOCAL void
  102. print_mem_stats (void)
  103. {
  104.   /* since this is only a debugging stub, we can cheat a little on the
  105.    * trace message mechanism... helpful 'cuz trace_message can't handle longs.
  106.    */
  107.   fprintf(stderr, "total_num_small = %ld\n", total_num_small);
  108.   fprintf(stderr, "total_bytes_small = %ld\n", total_bytes_small);
  109.   if (cur_num_small)
  110.     fprintf(stderr, "cur_num_small = %ld\n", cur_num_small);
  111.   fprintf(stderr, "max_num_small = %ld\n", max_num_small);
  112.   
  113. #ifdef NEED_ALLOC_MEDIUM
  114.   fprintf(stderr, "total_num_medium = %ld\n", total_num_medium);
  115.   fprintf(stderr, "total_bytes_medium = %ld\n", total_bytes_medium);
  116.   if (cur_num_medium)
  117.     fprintf(stderr, "cur_num_medium = %ld\n", cur_num_medium);
  118.   fprintf(stderr, "max_num_medium = %ld\n", max_num_medium);
  119. #endif
  120.   
  121.   fprintf(stderr, "total_num_sarray = %ld\n", total_num_sarray);
  122.   fprintf(stderr, "total_bytes_sarray = %ld\n", total_bytes_sarray);
  123.   if (cur_num_sarray)
  124.     fprintf(stderr, "cur_num_sarray = %ld\n", cur_num_sarray);
  125.   fprintf(stderr, "max_num_sarray = %ld\n", max_num_sarray);
  126.   
  127.   fprintf(stderr, "total_num_barray = %ld\n", total_num_barray);
  128.   fprintf(stderr, "total_bytes_barray = %ld\n", total_bytes_barray);
  129.   if (cur_num_barray)
  130.     fprintf(stderr, "cur_num_barray = %ld\n", cur_num_barray);
  131.   fprintf(stderr, "max_num_barray = %ld\n", max_num_barray);
  132. }
  133.  
  134. #endif /* MEM_STATS */
  135.  
  136.  
  137. LOCAL void
  138. out_of_memory (int which)
  139. /* Report an out-of-memory error and stop execution */
  140. /* If we compiled MEM_STATS support, report alloc requests before dying */
  141. {
  142. #ifdef MEM_STATS
  143.   if (methods->trace_level <= 0) /* don't do it if free_all() will */
  144.     print_mem_stats();        /* print optional memory usage statistics */
  145. #endif
  146.   ERREXIT1(methods, "Insufficient memory (case %d)", which);
  147. }
  148.  
  149.  
  150. /*
  151.  * Management of "small" objects.
  152.  * These are all-in-memory, and are in near-heap space on an 80x86.
  153.  */
  154.  
  155. typedef struct small_struct * small_ptr;
  156.  
  157. typedef struct small_struct {
  158.     small_ptr next;        /* next in list of allocated objects */
  159.       } small_hdr;
  160.  
  161. static small_ptr small_list;    /* head of list */
  162.  
  163.  
  164. METHODDEF void *
  165. alloc_small (size_t sizeofobject)
  166. /* Allocate a "small" object */
  167. {
  168.   small_ptr result;
  169.  
  170.   sizeofobject += SIZEOF(small_hdr); /* add space for header */
  171.  
  172. #ifdef MEM_STATS
  173.   total_num_small++;
  174.   total_bytes_small += sizeofobject + MALLOC_OVERHEAD;
  175.   cur_num_small++;
  176.   if (cur_num_small > max_num_small) max_num_small = cur_num_small;
  177. #endif
  178.  
  179.   result = (small_ptr) jget_small(sizeofobject);
  180.   if (result == NULL)
  181.     out_of_memory(1);
  182.  
  183.   result->next = small_list;
  184.   small_list = result;
  185.   result++;            /* advance past header */
  186.  
  187.   return (void *) result;
  188. }
  189.  
  190.  
  191. METHODDEF void
  192. free_small (void *ptr)
  193. /* Free a "small" object */
  194. {
  195.   small_ptr hdr;
  196.   small_ptr * llink;
  197.  
  198.   hdr = (small_ptr) ptr;
  199.   hdr--;            /* point back to header */
  200.  
  201.   /* Remove item from list -- linear search is fast enough */
  202.   llink = &small_list;
  203.   while (*llink != hdr) {
  204.     if (*llink == NULL)
  205.       ERREXIT(methods, "Bogus free_small request");
  206.     llink = &( (*llink)->next );
  207.   }
  208.   *llink = hdr->next;
  209.  
  210.   jfree_small((void *) hdr);
  211.  
  212. #ifdef MEM_STATS
  213.   cur_num_small--;
  214. #endif
  215. }
  216.  
  217.  
  218. /*
  219.  * Management of "medium-size" objects.
  220.  * These are just like small objects except they are in the FAR heap.
  221.  */
  222.  
  223. #ifdef NEED_ALLOC_MEDIUM
  224.  
  225. typedef struct medium_struct FAR * medium_ptr;
  226.  
  227. typedef struct medium_struct {
  228.     medium_ptr next;    /* next in list of allocated objects */
  229.       } medium_hdr;
  230.  
  231. static medium_ptr medium_list;    /* head of list */
  232.  
  233.  
  234. METHODDEF void FAR *
  235. alloc_medium (size_t sizeofobject)
  236. /* Allocate a "medium-size" object */
  237. {
  238.   medium_ptr result;
  239.  
  240.   sizeofobject += SIZEOF(medium_hdr); /* add space for header */
  241.  
  242. #ifdef MEM_STATS
  243.   total_num_medium++;
  244.   total_bytes_medium += sizeofobject + MALLOC_FAR_OVERHEAD;
  245.   cur_num_medium++;
  246.   if (cur_num_medium > max_num_medium) max_num_medium = cur_num_medium;
  247. #endif
  248.  
  249.   result = (medium_ptr) jget_large(sizeofobject);
  250.   if (result == NULL)
  251.     out_of_memory(2);
  252.  
  253.   result->next = medium_list;
  254.   medium_list = result;
  255.   result++;            /* advance past header */
  256.  
  257.   return (void FAR *) result;
  258. }
  259.  
  260.  
  261. METHODDEF void
  262. free_medium (void FAR *ptr)
  263. /* Free a "medium-size" object */
  264. {
  265.   medium_ptr hdr;
  266.   medium_ptr FAR * llink;
  267.  
  268.   hdr = (medium_ptr) ptr;
  269.   hdr--;            /* point back to header */
  270.  
  271.   /* Remove item from list -- linear search is fast enough */
  272.   llink = &medium_list;
  273.   while (*llink != hdr) {
  274.     if (*llink == NULL)
  275.       ERREXIT(methods, "Bogus free_medium request");
  276.     llink = &( (*llink)->next );
  277.   }
  278.   *llink = hdr->next;
  279.  
  280.   jfree_large((void FAR *) hdr);
  281.  
  282. #ifdef MEM_STATS
  283.   cur_num_medium--;
  284. #endif
  285. }
  286.  
  287. #endif /* NEED_ALLOC_MEDIUM */
  288.  
  289.  
  290. /*
  291.  * Management of "small" (all-in-memory) 2-D sample arrays.
  292.  * The pointers are in near heap, the samples themselves in FAR heap.
  293.  * The header structure is adjacent to the row pointers.
  294.  * To minimize allocation overhead and to allow I/O of large contiguous
  295.  * blocks, we allocate the sample rows in groups of as many rows as possible
  296.  * without exceeding MAX_ALLOC_CHUNK total bytes per allocation request.
  297.  * Note that the big-array control routines, later in this file, know about
  298.  * this chunking of rows ... and also how to get the rowsperchunk value!
  299.  */
  300.  
  301. typedef struct small_sarray_struct * small_sarray_ptr;
  302.  
  303. typedef struct small_sarray_struct {
  304.     small_sarray_ptr next;    /* next in list of allocated sarrays */
  305.     long numrows;        /* # of rows in this array */
  306.     long rowsperchunk;    /* max # of rows per allocation chunk */
  307.       } small_sarray_hdr;
  308.  
  309. static small_sarray_ptr small_sarray_list; /* head of list */
  310.  
  311.  
  312. METHODDEF JSAMPARRAY
  313. alloc_small_sarray (long samplesperrow, long numrows)
  314. /* Allocate a "small" (all-in-memory) 2-D sample array */
  315. {
  316.   small_sarray_ptr hdr;
  317.   JSAMPARRAY result;
  318.   JSAMPROW workspace;
  319.   long rowsperchunk, currow, i;
  320.  
  321. #ifdef MEM_STATS
  322.   total_num_sarray++;
  323.   cur_num_sarray++;
  324.   if (cur_num_sarray > max_num_sarray) max_num_sarray = cur_num_sarray;
  325. #endif
  326.  
  327.   /* Calculate max # of rows allowed in one allocation chunk */
  328.   rowsperchunk = MAX_ALLOC_CHUNK / (samplesperrow * SIZEOF(JSAMPLE));
  329.   if (rowsperchunk <= 0)
  330.       ERREXIT(methods, "Image too wide for this implementation");
  331.  
  332.   /* Get space for header and row pointers; this is always "near" on 80x86 */
  333.   hdr = (small_sarray_ptr) alloc_small((size_t) (numrows * SIZEOF(JSAMPROW)
  334.                          + SIZEOF(small_sarray_hdr)));
  335.  
  336.   result = (JSAMPARRAY) (hdr+1); /* advance past header */
  337.  
  338.   /* Insert into list now so free_all does right thing if I fail */
  339.   /* after allocating only some of the rows... */
  340.   hdr->next = small_sarray_list;
  341.   hdr->numrows = 0;
  342.   hdr->rowsperchunk = rowsperchunk;
  343.   small_sarray_list = hdr;
  344.  
  345.   /* Get the rows themselves; on 80x86 these are "far" */
  346.   currow = 0;
  347.   while (currow < numrows) {
  348.     rowsperchunk = MIN(rowsperchunk, numrows - currow);
  349. #ifdef MEM_STATS
  350.     total_bytes_sarray += rowsperchunk * samplesperrow * SIZEOF(JSAMPLE)
  351.               + MALLOC_FAR_OVERHEAD;
  352. #endif
  353.     workspace = (JSAMPROW) jget_large((size_t) (rowsperchunk * samplesperrow
  354.                         * SIZEOF(JSAMPLE)));
  355.     if (workspace == NULL)
  356.       out_of_memory(3);
  357.     for (i = rowsperchunk; i > 0; i--) {
  358.       result[currow++] = workspace;
  359.       workspace += samplesperrow;
  360.     }
  361.     hdr->numrows = currow;
  362.   }
  363.  
  364.   return result;
  365. }
  366.  
  367.  
  368. METHODDEF void
  369. free_small_sarray (JSAMPARRAY ptr)
  370. /* Free a "small" (all-in-memory) 2-D sample array */
  371. {
  372.   small_sarray_ptr hdr;
  373.   small_sarray_ptr * llink;
  374.   long i;
  375.  
  376.   hdr = (small_sarray_ptr) ptr;
  377.   hdr--;            /* point back to header */
  378.  
  379.   /* Remove item from list -- linear search is fast enough */
  380.   llink = &small_sarray_list;
  381.   while (*llink != hdr) {
  382.     if (*llink == NULL)
  383.       ERREXIT(methods, "Bogus free_small_sarray request");
  384.     llink = &( (*llink)->next );
  385.   }
  386.   *llink = hdr->next;
  387.  
  388.   /* Free the rows themselves; on 80x86 these are "far" */
  389.   /* Note we only free the row-group headers! */
  390.   for (i = 0; i < hdr->numrows; i += hdr->rowsperchunk) {
  391.     jfree_large((void FAR *) ptr[i]);
  392.   }
  393.  
  394.   /* Free header and row pointers */
  395.   free_small((void *) hdr);
  396.  
  397. #ifdef MEM_STATS
  398.   cur_num_sarray--;
  399. #endif
  400. }
  401.  
  402.  
  403. /*
  404.  * Management of "small" (all-in-memory) 2-D coefficient-block arrays.
  405.  * This is essentially the same as the code for sample arrays, above.
  406.  */
  407.  
  408. typedef struct small_barray_struct * small_barray_ptr;
  409.  
  410. typedef struct small_barray_struct {
  411.     small_barray_ptr next;    /* next in list of allocated barrays */
  412.     long numrows;        /* # of rows in this array */
  413.     long rowsperchunk;    /* max # of rows per allocation chunk */
  414.       } small_barray_hdr;
  415.  
  416. static small_barray_ptr small_barray_list; /* head of list */
  417.  
  418.  
  419. METHODDEF JBLOCKARRAY
  420. alloc_small_barray (long blocksperrow, long numrows)
  421. /* Allocate a "small" (all-in-memory) 2-D coefficient-block array */
  422. {
  423.   small_barray_ptr hdr;
  424.   JBLOCKARRAY result;
  425.   JBLOCKROW workspace;
  426.   long rowsperchunk, currow, i;
  427.  
  428. #ifdef MEM_STATS
  429.   total_num_barray++;
  430.   cur_num_barray++;
  431.   if (cur_num_barray > max_num_barray) max_num_barray = cur_num_barray;
  432. #endif
  433.  
  434.   /* Calculate max # of rows allowed in one allocation chunk */
  435.   rowsperchunk = MAX_ALLOC_CHUNK / (blocksperrow * SIZEOF(JBLOCK));
  436.   if (rowsperchunk <= 0)
  437.       ERREXIT(methods, "Image too wide for this implementation");
  438.  
  439.   /* Get space for header and row pointers; this is always "near" on 80x86 */
  440.   hdr = (small_barray_ptr) alloc_small((size_t) (numrows * SIZEOF(JBLOCKROW)
  441.                          + SIZEOF(small_barray_hdr)));
  442.  
  443.   result = (JBLOCKARRAY) (hdr+1); /* advance past header */
  444.  
  445.   /* Insert into list now so free_all does right thing if I fail */
  446.   /* after allocating only some of the rows... */
  447.   hdr->next = small_barray_list;
  448.   hdr->numrows = 0;
  449.   hdr->rowsperchunk = rowsperchunk;
  450.   small_barray_list = hdr;
  451.  
  452.   /* Get the rows themselves; on 80x86 these are "far" */
  453.   currow = 0;
  454.   while (currow < numrows) {
  455.     rowsperchunk = MIN(rowsperchunk, numrows - currow);
  456. #ifdef MEM_STATS
  457.     total_bytes_barray += rowsperchunk * blocksperrow * SIZEOF(JBLOCK)
  458.               + MALLOC_FAR_OVERHEAD;
  459. #endif
  460.     workspace = (JBLOCKROW) jget_large((size_t) (rowsperchunk * blocksperrow
  461.                          * SIZEOF(JBLOCK)));
  462.     if (workspace == NULL)
  463.       out_of_memory(4);
  464.     for (i = rowsperchunk; i > 0; i--) {
  465.       result[currow++] = workspace;
  466.       workspace += blocksperrow;
  467.     }
  468.     hdr->numrows = currow;
  469.   }
  470.  
  471.   return result;
  472. }
  473.  
  474.  
  475. METHODDEF void
  476. free_small_barray (JBLOCKARRAY ptr)
  477. /* Free a "small" (all-in-memory) 2-D coefficient-block array */
  478. {
  479.   small_barray_ptr hdr;
  480.   small_barray_ptr * llink;
  481.   long i;
  482.  
  483.   hdr = (small_barray_ptr) ptr;
  484.   hdr--;            /* point back to header */
  485.  
  486.   /* Remove item from list -- linear search is fast enough */
  487.   llink = &small_barray_list;
  488.   while (*llink != hdr) {
  489.     if (*llink == NULL)
  490.       ERREXIT(methods, "Bogus free_small_barray request");
  491.     llink = &( (*llink)->next );
  492.   }
  493.   *llink = hdr->next;
  494.  
  495.   /* Free the rows themselves; on 80x86 these are "far" */
  496.   /* Note we only free the row-group headers! */
  497.   for (i = 0; i < hdr->numrows; i += hdr->rowsperchunk) {
  498.     jfree_large((void FAR *) ptr[i]);
  499.   }
  500.  
  501.   /* Free header and row pointers */
  502.   free_small((void *) hdr);
  503.  
  504. #ifdef MEM_STATS
  505.   cur_num_barray--;
  506. #endif
  507. }
  508.  
  509.  
  510.  
  511. /*
  512.  * About "big" array management:
  513.  *
  514.  * To allow machines with limited memory to handle large images,
  515.  * all processing in the JPEG system is done a few pixel or block rows
  516.  * at a time.  The above "small" array routines are only used to allocate
  517.  * strip buffers (as wide as the image, but just a few rows high).
  518.  * In some cases multiple passes must be made over the data.  In these
  519.  * cases the "big" array routines are used.  The array is still accessed
  520.  * a strip at a time, but the memory manager must save the whole array
  521.  * for repeated accesses.  The intended implementation is that there is
  522.  * a strip buffer in memory (as high as is possible given the desired memory
  523.  * limit), plus a backing file that holds the rest of the array.
  524.  *
  525.  * The request_big_array routines are told the total size of the image (in case
  526.  * it is useful to know the total file size that will be needed).  They are
  527.  * also given the unit height, which is the number of rows that will be
  528.  * accessed at once; the in-memory buffer should be made a multiple of
  529.  * this height for best efficiency.
  530.  *
  531.  * The request routines create control blocks (and may open backing files),
  532.  * but they don't create the in-memory buffers.  This is postponed until
  533.  * alloc_big_arrays is called.  At that time the total amount of space needed
  534.  * is known (approximately, anyway), so free memory can be divided up fairly.
  535.  *
  536.  * The access_big_array routines are responsible for making a specific strip
  537.  * area accessible (after reading or writing the backing file, if necessary).
  538.  * Note that the access routines are told whether the caller intends to modify
  539.  * the accessed strip; during a read-only pass this saves having to rewrite
  540.  * data to disk.
  541.  *
  542.  * The typical access pattern is one top-to-bottom pass to write the data,
  543.  * followed by one or more read-only top-to-bottom passes.  However, other
  544.  * access patterns may occur while reading.  For example, translation of image
  545.  * formats that use bottom-to-top scan order will require bottom-to-top read
  546.  * passes.  The memory manager need not support multiple write passes nor
  547.  * funny write orders (meaning that rearranging rows must be handled while
  548.  * reading data out of the big array, not while putting it in).
  549.  *
  550.  * In current usage, the access requests are always for nonoverlapping strips;
  551.  * that is, successive access start_row numbers always differ by exactly the
  552.  * unitheight.  This allows fairly simple buffer dump/reload logic if the
  553.  * in-memory buffer is made a multiple of the unitheight.  It would be
  554.  * possible to keep subsampled rather than fullsize data in the "big" arrays,
  555.  * thus reducing temp file size, if we supported overlapping strip access
  556.  * (access requests differing by less than the unitheight).  At the moment
  557.  * I don't believe this is worth the extra complexity.
  558.  */
  559.  
  560.  
  561.  
  562. /* The control blocks for virtual arrays.
  563.  * System-dependent info for the associated backing store is hidden inside
  564.  * the backing_store_info struct.
  565.  */
  566.  
  567. struct big_sarray_control {
  568.     long rows_in_array;    /* total virtual array height */
  569.     long samplesperrow;    /* width of array (and of memory buffer) */
  570.     long unitheight;    /* # of rows accessed by access_big_sarray() */
  571.     JSAMPARRAY mem_buffer;    /* the in-memory buffer */
  572.     long rows_in_mem;    /* height of memory buffer */
  573.     long rowsperchunk;    /* allocation chunk size in mem_buffer */
  574.     long cur_start_row;    /* first logical row # in the buffer */
  575.     boolean dirty;        /* do current buffer contents need written? */
  576.     boolean b_s_open;    /* is backing-store data valid? */
  577.     big_sarray_ptr next;    /* link to next big sarray control block */
  578.     backing_store_info b_s_info; /* System-dependent control info */
  579. };
  580.  
  581. static big_sarray_ptr big_sarray_list; /* head of list */
  582.  
  583. struct big_barray_control {
  584.     long rows_in_array;    /* total virtual array height */
  585.     long blocksperrow;    /* width of array (and of memory buffer) */
  586.     long unitheight;    /* # of rows accessed by access_big_barray() */
  587.     JBLOCKARRAY mem_buffer;    /* the in-memory buffer */
  588.     long rows_in_mem;    /* height of memory buffer */
  589.     long rowsperchunk;    /* allocation chunk size in mem_buffer */
  590.     long cur_start_row;    /* first logical row # in the buffer */
  591.     boolean dirty;        /* do current buffer contents need written? */
  592.     boolean b_s_open;    /* is backing-store data valid? */
  593.     big_barray_ptr next;    /* link to next big barray control block */
  594.     backing_store_info b_s_info; /* System-dependent control info */
  595. };
  596.  
  597. static big_barray_ptr big_barray_list; /* head of list */
  598.  
  599.  
  600. METHODDEF big_sarray_ptr
  601. request_big_sarray (long samplesperrow, long numrows, long unitheight)
  602. /* Request a "big" (virtual-memory) 2-D sample array */
  603. {
  604.   big_sarray_ptr result;
  605.  
  606.   /* get control block */
  607.   result = (big_sarray_ptr) alloc_small(SIZEOF(struct big_sarray_control));
  608.  
  609.   result->rows_in_array = numrows;
  610.   result->samplesperrow = samplesperrow;
  611.   result->unitheight = unitheight;
  612.   result->mem_buffer = NULL;    /* marks array not yet realized */
  613.   result->b_s_open = FALSE;    /* no associated backing-store object */
  614.   result->next = big_sarray_list; /* add to list of big arrays */
  615.   big_sarray_list = result;
  616.  
  617.   return result;
  618. }
  619.  
  620.  
  621. METHODDEF big_barray_ptr
  622. request_big_barray (long blocksperrow, long numrows, long unitheight)
  623. /* Request a "big" (virtual-memory) 2-D coefficient-block array */
  624. {
  625.   big_barray_ptr result;
  626.  
  627.   /* get control block */
  628.   result = (big_barray_ptr) alloc_small(SIZEOF(struct big_barray_control));
  629.  
  630.   result->rows_in_array = numrows;
  631.   result->blocksperrow = blocksperrow;
  632.   result->unitheight = unitheight;
  633.   result->mem_buffer = NULL;    /* marks array not yet realized */
  634.   result->b_s_open = FALSE;    /* no associated backing-store object */
  635.   result->next = big_barray_list; /* add to list of big arrays */
  636.   big_barray_list = result;
  637.  
  638.   return result;
  639. }
  640.  
  641.  
  642. METHODDEF void
  643. alloc_big_arrays (long extra_small_samples, long extra_small_blocks,
  644.           long extra_medium_space)
  645. /* Allocate the in-memory buffers for any unrealized "big" arrays */
  646. /* 'extra' values are upper bounds for total future small-array requests */
  647. /* and far-heap requests */
  648. {
  649.   long total_extra_space = extra_small_samples * SIZEOF(JSAMPLE)
  650.                + extra_small_blocks * SIZEOF(JBLOCK)
  651.                + extra_medium_space;
  652.   long space_per_unitheight, maximum_space, avail_mem;
  653.   long unitheights, max_unitheights;
  654.   big_sarray_ptr sptr;
  655.   big_barray_ptr bptr;
  656.  
  657.   /* Compute the minimum space needed (unitheight rows in each buffer)
  658.    * and the maximum space needed (full image height in each buffer).
  659.    * These may be of use to the system-dependent jmem_available routine.
  660.    */
  661.   space_per_unitheight = 0;
  662.   maximum_space = total_extra_space;
  663.   for (sptr = big_sarray_list; sptr != NULL; sptr = sptr->next) {
  664.     if (sptr->mem_buffer == NULL) { /* if not realized yet */
  665.       space_per_unitheight += sptr->unitheight *
  666.                   sptr->samplesperrow * SIZEOF(JSAMPLE);
  667.       maximum_space += sptr->rows_in_array *
  668.                sptr->samplesperrow * SIZEOF(JSAMPLE);
  669.     }
  670.   }
  671.   for (bptr = big_barray_list; bptr != NULL; bptr = bptr->next) {
  672.     if (bptr->mem_buffer == NULL) { /* if not realized yet */
  673.       space_per_unitheight += bptr->unitheight *
  674.                   bptr->blocksperrow * SIZEOF(JBLOCK);
  675.       maximum_space += bptr->rows_in_array *
  676.                bptr->blocksperrow * SIZEOF(JBLOCK);
  677.     }
  678.   }
  679.  
  680.   if (space_per_unitheight <= 0)
  681.     return;            /* no unrealized arrays, no work */
  682.  
  683.   /* Determine amount of memory to actually use; this is system-dependent. */
  684.   avail_mem = jmem_available(space_per_unitheight + total_extra_space,
  685.                  maximum_space);
  686.  
  687.   /* If the maximum space needed is available, make all the buffers full
  688.    * height; otherwise parcel it out with the same number of unitheights
  689.    * in each buffer.
  690.    */
  691.   if (avail_mem >= maximum_space)
  692.     max_unitheights = 1000000000L;
  693.   else {
  694.     max_unitheights = (avail_mem - total_extra_space) / space_per_unitheight;
  695.     /* If there doesn't seem to be enough space, try to get the minimum
  696.      * anyway.  This allows a "stub" implementation of jmem_available().
  697.      */
  698.     if (max_unitheights <= 0)
  699.       max_unitheights = 1;
  700.   }
  701.  
  702.   /* Allocate the in-memory buffers and initialize backing store as needed. */
  703.  
  704.   for (sptr = big_sarray_list; sptr != NULL; sptr = sptr->next) {
  705.     if (sptr->mem_buffer == NULL) { /* if not realized yet */
  706.       unitheights = (sptr->rows_in_array + sptr->unitheight - 1L)
  707.             / sptr->unitheight;
  708.       if (unitheights <= max_unitheights) {
  709.     /* This buffer fits in memory */
  710.     sptr->rows_in_mem = sptr->rows_in_array;
  711.       } else {
  712.     /* It doesn't fit in memory, create backing store. */
  713.     sptr->rows_in_mem = max_unitheights * sptr->unitheight;
  714.     jopen_backing_store(& sptr->b_s_info,
  715.                 sptr->rows_in_array
  716.                 * sptr->samplesperrow * SIZEOF(JSAMPLE));
  717.     sptr->b_s_open = TRUE;
  718.       }
  719.       sptr->mem_buffer = alloc_small_sarray(sptr->samplesperrow,
  720.                         sptr->rows_in_mem);
  721.       /* Reach into the small_sarray header and get the rowsperchunk field.
  722.        * Yes, I know, this is horrible coding practice.
  723.        */
  724.       sptr->rowsperchunk =
  725.     ((small_sarray_ptr) sptr->mem_buffer)[-1].rowsperchunk;
  726.       sptr->cur_start_row = 0;
  727.       sptr->dirty = FALSE;
  728.     }
  729.   }
  730.  
  731.   for (bptr = big_barray_list; bptr != NULL; bptr = bptr->next) {
  732.     if (bptr->mem_buffer == NULL) { /* if not realized yet */
  733.       unitheights = (bptr->rows_in_array + bptr->unitheight - 1L)
  734.             / bptr->unitheight;
  735.       if (unitheights <= max_unitheights) {
  736.     /* This buffer fits in memory */
  737.     bptr->rows_in_mem = bptr->rows_in_array;
  738.       } else {
  739.     /* It doesn't fit in memory, create backing store. */
  740.     bptr->rows_in_mem = max_unitheights * bptr->unitheight;
  741.     jopen_backing_store(& bptr->b_s_info,
  742.                 bptr->rows_in_array
  743.                 * bptr->blocksperrow * SIZEOF(JBLOCK));
  744.     bptr->b_s_open = TRUE;
  745.       }
  746.       bptr->mem_buffer = alloc_small_barray(bptr->blocksperrow,
  747.                         bptr->rows_in_mem);
  748.       /* Reach into the small_barray header and get the rowsperchunk field. */
  749.       bptr->rowsperchunk =
  750.     ((small_barray_ptr) bptr->mem_buffer)[-1].rowsperchunk;
  751.       bptr->cur_start_row = 0;
  752.       bptr->dirty = FALSE;
  753.     }
  754.   }
  755. }
  756.  
  757.  
  758. LOCAL void
  759. do_sarray_io (big_sarray_ptr ptr, boolean writing)
  760. /* Do backing store read or write of a "big" sample array */
  761. {
  762.   long bytesperrow, file_offset, byte_count, rows, i;
  763.  
  764.   bytesperrow = ptr->samplesperrow * SIZEOF(JSAMPLE);
  765.   file_offset = ptr->cur_start_row * bytesperrow;
  766.   /* Loop to read or write each allocation chunk in mem_buffer */
  767.   for (i = 0; i < ptr->rows_in_mem; i += ptr->rowsperchunk) {
  768.     /* One chunk, but check for short chunk at end of buffer */
  769.     rows = MIN(ptr->rowsperchunk, ptr->rows_in_mem - i);
  770.     /* Transfer no more than fits in file */
  771.     rows = MIN(rows, ptr->rows_in_array - (ptr->cur_start_row + i));
  772.     if (rows <= 0)        /* this chunk might be past end of file! */
  773.       break;
  774.     byte_count = rows * bytesperrow;
  775.     if (writing)
  776.       (*ptr->b_s_info.write_backing_store) (& ptr->b_s_info,
  777.                         (void FAR *) ptr->mem_buffer[i],
  778.                         file_offset, byte_count);
  779.     else
  780.       (*ptr->b_s_info.read_backing_store) (& ptr->b_s_info,
  781.                        (void FAR *) ptr->mem_buffer[i],
  782.                        file_offset, byte_count);
  783.     file_offset += byte_count;
  784.   }
  785. }
  786.  
  787.  
  788. LOCAL void
  789. do_barray_io (big_barray_ptr ptr, boolean writing)
  790. /* Do backing store read or write of a "big" coefficient-block array */
  791. {
  792.   long bytesperrow, file_offset, byte_count, rows, i;
  793.  
  794.   bytesperrow = ptr->blocksperrow * SIZEOF(JBLOCK);
  795.   file_offset = ptr->cur_start_row * bytesperrow;
  796.   /* Loop to read or write each allocation chunk in mem_buffer */
  797.   for (i = 0; i < ptr->rows_in_mem; i += ptr->rowsperchunk) {
  798.     /* One chunk, but check for short chunk at end of buffer */
  799.     rows = MIN(ptr->rowsperchunk, ptr->rows_in_mem - i);
  800.     /* Transfer no more than fits in file */
  801.     rows = MIN(rows, ptr->rows_in_array - (ptr->cur_start_row + i));
  802.     if (rows <= 0)        /* this chunk might be past end of file! */
  803.       break;
  804.     byte_count = rows * bytesperrow;
  805.     if (writing)
  806.       (*ptr->b_s_info.write_backing_store) (& ptr->b_s_info,
  807.                         (void FAR *) ptr->mem_buffer[i],
  808.                         file_offset, byte_count);
  809.     else
  810.       (*ptr->b_s_info.read_backing_store) (& ptr->b_s_info,
  811.                        (void FAR *) ptr->mem_buffer[i],
  812.                        file_offset, byte_count);
  813.     file_offset += byte_count;
  814.   }
  815. }
  816.  
  817.  
  818. METHODDEF JSAMPARRAY
  819. access_big_sarray (big_sarray_ptr ptr, long start_row, boolean writable)
  820. /* Access the part of a "big" sample array starting at start_row */
  821. /* and extending for ptr->unitheight rows.  writable is true if  */
  822. /* caller intends to modify the accessed area. */
  823. {
  824.   /* debugging check */
  825.   if (start_row < 0 || start_row+ptr->unitheight > ptr->rows_in_array ||
  826.       ptr->mem_buffer == NULL)
  827.     ERREXIT(methods, "Bogus access_big_sarray request");
  828.  
  829.   /* Make the desired part of the virtual array accessible */
  830.   if (start_row < ptr->cur_start_row ||
  831.       start_row+ptr->unitheight > ptr->cur_start_row+ptr->rows_in_mem) {
  832.     if (! ptr->b_s_open)
  833.       ERREXIT(methods, "Virtual array controller messed up");
  834.     /* Flush old buffer contents if necessary */
  835.     if (ptr->dirty) {
  836.       do_sarray_io(ptr, TRUE);
  837.       ptr->dirty = FALSE;
  838.     }
  839.     /* Decide what part of virtual array to access.
  840.      * Algorithm: if target address > current window, assume forward scan,
  841.      * load starting at target address.  If target address < current window,
  842.      * assume backward scan, load so that target address is top of window.
  843.      * Note that when switching from forward write to forward read, will have
  844.      * start_row = 0, so the limiting case applies and we load from 0 anyway.
  845.      */
  846.     if (start_row > ptr->cur_start_row) {
  847.       ptr->cur_start_row = start_row;
  848.     } else {
  849.       ptr->cur_start_row = start_row + ptr->unitheight - ptr->rows_in_mem;
  850.       if (ptr->cur_start_row < 0)
  851.     ptr->cur_start_row = 0;    /* don't fall off front end of file */
  852.     }
  853.     /* If reading, read in the selected part of the array. 
  854.      * If we are writing, we need not pre-read the selected portion,
  855.      * since the access sequence constraints ensure it would be garbage.
  856.      */
  857.     if (! writable) {
  858.       do_sarray_io(ptr, FALSE);
  859.     }
  860.   }
  861.   /* Flag the buffer dirty if caller will write in it */
  862.   if (writable)
  863.     ptr->dirty = TRUE;
  864.   /* Return address of proper part of the buffer */
  865.   return ptr->mem_buffer + (start_row - ptr->cur_start_row);
  866. }
  867.  
  868.  
  869. METHODDEF JBLOCKARRAY
  870. access_big_barray (big_barray_ptr ptr, long start_row, boolean writable)
  871. /* Access the part of a "big" coefficient-block array starting at start_row */
  872. /* and extending for ptr->unitheight rows.  writable is true if  */
  873. /* caller intends to modify the accessed area. */
  874. {
  875.   /* debugging check */
  876.   if (start_row < 0 || start_row+ptr->unitheight > ptr->rows_in_array ||
  877.       ptr->mem_buffer == NULL)
  878.     ERREXIT(methods, "Bogus access_big_barray request");
  879.  
  880.   /* Make the desired part of the virtual array accessible */
  881.   if (start_row < ptr->cur_start_row ||
  882.       start_row+ptr->unitheight > ptr->cur_start_row+ptr->rows_in_mem) {
  883.     if (! ptr->b_s_open)
  884.       ERREXIT(methods, "Virtual array controller messed up");
  885.     /* Flush old buffer contents if necessary */
  886.     if (ptr->dirty) {
  887.       do_barray_io(ptr, TRUE);
  888.       ptr->dirty = FALSE;
  889.     }
  890.     /* Decide what part of virtual array to access.
  891.      * Algorithm: if target address > current window, assume forward scan,
  892.      * load starting at target address.  If target address < current window,
  893.      * assume backward scan, load so that target address is top of window.
  894.      * Note that when switching from forward write to forward read, will have
  895.      * start_row = 0, so the limiting case applies and we load from 0 anyway.
  896.      */
  897.     if (start_row > ptr->cur_start_row) {
  898.       ptr->cur_start_row = start_row;
  899.     } else {
  900.       ptr->cur_start_row = start_row + ptr->unitheight - ptr->rows_in_mem;
  901.       if (ptr->cur_start_row < 0)
  902.     ptr->cur_start_row = 0;    /* don't fall off front end of file */
  903.     }
  904.     /* If reading, read in the selected part of the array. 
  905.      * If we are writing, we need not pre-read the selected portion,
  906.      * since the access sequence constraints ensure it would be garbage.
  907.      */
  908.     if (! writable) {
  909.       do_barray_io(ptr, FALSE);
  910.     }
  911.   }
  912.   /* Flag the buffer dirty if caller will write in it */
  913.   if (writable)
  914.     ptr->dirty = TRUE;
  915.   /* Return address of proper part of the buffer */
  916.   return ptr->mem_buffer + (start_row - ptr->cur_start_row);
  917. }
  918.  
  919.  
  920. METHODDEF void
  921. free_big_sarray (big_sarray_ptr ptr)
  922. /* Free a "big" (virtual-memory) 2-D sample array */
  923. {
  924.   big_sarray_ptr * llink;
  925.  
  926.   /* Remove item from list -- linear search is fast enough */
  927.   llink = &big_sarray_list;
  928.   while (*llink != ptr) {
  929.     if (*llink == NULL)
  930.       ERREXIT(methods, "Bogus free_big_sarray request");
  931.     llink = &( (*llink)->next );
  932.   }
  933.   *llink = ptr->next;
  934.  
  935.   if (ptr->b_s_open)        /* there may be no backing store */
  936.     (*ptr->b_s_info.close_backing_store) (& ptr->b_s_info);
  937.  
  938.   if (ptr->mem_buffer != NULL)    /* just in case never realized */
  939.     free_small_sarray(ptr->mem_buffer);
  940.  
  941.   free_small((void *) ptr);    /* free the control block too */
  942. }
  943.  
  944.  
  945. METHODDEF void
  946. free_big_barray (big_barray_ptr ptr)
  947. /* Free a "big" (virtual-memory) 2-D coefficient-block array */
  948. {
  949.   big_barray_ptr * llink;
  950.  
  951.   /* Remove item from list -- linear search is fast enough */
  952.   llink = &big_barray_list;
  953.   while (*llink != ptr) {
  954.     if (*llink == NULL)
  955.       ERREXIT(methods, "Bogus free_big_barray request");
  956.     llink = &( (*llink)->next );
  957.   }
  958.   *llink = ptr->next;
  959.  
  960.   if (ptr->b_s_open)        /* there may be no backing store */
  961.     (*ptr->b_s_info.close_backing_store) (& ptr->b_s_info);
  962.  
  963.   if (ptr->mem_buffer != NULL)    /* just in case never realized */
  964.     free_small_barray(ptr->mem_buffer);
  965.  
  966.   free_small((void *) ptr);    /* free the control block too */
  967. }
  968.  
  969.  
  970. /*
  971.  * Cleanup: free anything that's been allocated since jselmemmgr().
  972.  */
  973.  
  974. METHODDEF void
  975. free_all (void)
  976. {
  977.   /* First free any open "big" arrays -- these may release small arrays */
  978.   while (big_sarray_list != NULL)
  979.     free_big_sarray(big_sarray_list);
  980.   while (big_barray_list != NULL)
  981.     free_big_barray(big_barray_list);
  982.   /* Free any open small arrays -- these may release small objects */
  983.   /* +1's are because we must pass a pointer to the data, not the header */
  984.   while (small_sarray_list != NULL)
  985.     free_small_sarray((JSAMPARRAY) (small_sarray_list + 1));
  986.   while (small_barray_list != NULL)
  987.     free_small_barray((JBLOCKARRAY) (small_barray_list + 1));
  988.   /* Free any remaining small objects */
  989.   while (small_list != NULL)
  990.     free_small((void *) (small_list + 1));
  991. #ifdef NEED_ALLOC_MEDIUM
  992.   while (medium_list != NULL)
  993.     free_medium((void FAR *) (medium_list + 1));
  994. #endif
  995.  
  996.   jmem_term();            /* system-dependent cleanup */
  997.  
  998. #ifdef MEM_STATS
  999.   if (methods->trace_level > 0)
  1000.     print_mem_stats();        /* print optional memory usage statistics */
  1001. #endif
  1002. }
  1003.  
  1004.  
  1005. /*
  1006.  * The method selection routine for virtual memory systems.
  1007.  * The system-dependent setup routine should call this routine
  1008.  * to install the necessary method pointers in the supplied struct.
  1009.  */
  1010.  
  1011. GLOBAL void
  1012. jselmemmgr (external_methods_ptr emethods)
  1013. {
  1014.   methods = emethods;        /* save struct addr for error exit access */
  1015.  
  1016.   emethods->alloc_small = alloc_small;
  1017.   emethods->free_small = free_small;
  1018. #ifdef NEED_ALLOC_MEDIUM
  1019.   emethods->alloc_medium = alloc_medium;
  1020.   emethods->free_medium = free_medium;
  1021. #else
  1022.   emethods->alloc_medium = alloc_small;
  1023.   emethods->free_medium = free_small;
  1024. #endif
  1025.   emethods->alloc_small_sarray = alloc_small_sarray;
  1026.   emethods->free_small_sarray = free_small_sarray;
  1027.   emethods->alloc_small_barray = alloc_small_barray;
  1028.   emethods->free_small_barray = free_small_barray;
  1029.   emethods->request_big_sarray = request_big_sarray;
  1030.   emethods->request_big_barray = request_big_barray;
  1031.   emethods->alloc_big_arrays = alloc_big_arrays;
  1032.   emethods->access_big_sarray = access_big_sarray;
  1033.   emethods->access_big_barray = access_big_barray;
  1034.   emethods->free_big_sarray = free_big_sarray;
  1035.   emethods->free_big_barray = free_big_barray;
  1036.   emethods->free_all = free_all;
  1037.  
  1038.   /* Initialize list headers to empty */
  1039.   small_list = NULL;
  1040. #ifdef NEED_ALLOC_MEDIUM
  1041.   medium_list = NULL;
  1042. #endif
  1043.   small_sarray_list = NULL;
  1044.   small_barray_list = NULL;
  1045.   big_sarray_list = NULL;
  1046.   big_barray_list = NULL;
  1047.  
  1048.   jmem_init(emethods);        /* system-dependent initialization */
  1049. }
  1050.